/*
 * Directorio.cpp
 *
 *  Created on: Feb 13, 2010
 *      Author: jgrande
 */

#include "DirectorioImpl.h"

namespace aplicacion {

DirectorioImpl::DirectorioImpl() {
	string hostname = ConfigSingleton::instance().get<string>("hostDirectorio");
	logger << LogType::LOG_DEBUG_1 << "creando cliente RPC con host " <<
			hostname << logger.endl;
	client = clnt_create (hostname.c_str(),
			DIRECTORIO_RPC_PROG, DIRECTORIO_RPC_VERS, "udp");
	if(!client) {
		logger << LogType::LOG_ERROR << "error! imposible conectar con el directorio." << logger.endl;
	} else {
		logger << "directorio creado, cliente = " << (void*)client << logger.endl;
	}
}

DirectorioImpl::~DirectorioImpl() {
	if(client!=NULL) {
		clnt_destroy(client);
	}
}

bool DirectorioImpl::registrarNuevo (InformacionCreacionUniverso& aRegistrar) {
	u_int *result;

	result = crear_universo_1(
			aRegistrar._minPlanetas,
			aRegistrar._maxPlanetas,
			aRegistrar._timeout,
			client);

	if(result!=(u_int*)NULL && *result!=(u_int)-1) {
		aRegistrar._idUniverso = *result;
		return true;
	} else {
		return false;
	}
}

bool DirectorioImpl::registrarNuevo (JuegoDTO& aRegistrar) {
	u_int *result;

	result = crear_juego_1(
			aRegistrar.min,
			aRegistrar.max,
			aRegistrar.idUniverso.getId(),
			client);

	if(result!=(u_int*)NULL && *result!=(u_int)-1) {
		aRegistrar.idJuego = *result;
		return true;
	} else {
		return false;
	}
}

bool DirectorioImpl::add(const InformacionCreacionUniverso& universo,
		InformacionPlaneta& aAgregar) {
	u_int *result;
	u_int dirLocal = ConfigSingleton::instance().getDirLocal();

	result = suscribir_planeta_1(
			universo._idUniverso,
			dirLocal,
			aAgregar._tamanio,
			aAgregar._posicion.getX(),
			aAgregar._posicion.getY(),
			client);

	if(result!=(u_int*)NULL && *result!=(u_int)-1) {
		aAgregar._identificador = IdPlaneta(*result, universo._idUniverso);
		return true;
	} else {
		return false;
	}
}

bool DirectorioImpl::add(const JuegoDTO& juego, JugadorDTO& aAgregar) {
	u_int *result;
	u_int dirLocal = ConfigSingleton::instance().getDirLocal();

	result = suscribir_jugador_1(
			juego.idJuego.getId(),
			dirLocal,
			client);

	if(result!=(u_int*)NULL && *result!=(u_int)-1) {
		aAgregar.idJugador = IdJugador(*result, juego.idJuego);
		return true;
	} else {
		return false;
	}
}

bool DirectorioImpl::remove(const JugadorDTO& aQuitar) {
	bool_t *result;

	result = cancelar_jugador_1(
			aQuitar.idJugador.getId(),
			aQuitar.idJugador.getContainerId(),
			client);

	return (result!=(bool_t*)NULL && *result==TRUE);
}

bool DirectorioImpl::esperarComienzo(const JugadorDTO& jugador) {
	bool_t *result;
	uint8_t datos[MAX_DATOS];

	result = jugador_listo_1(
			jugador.idJugador.getId(),
			jugador.idJugador.getContainerId(),
			client);

	if(result!=(bool_t*)NULL && *result==TRUE) {
		MiddlewareCliente& mw = MiddlewareClienteSingleton::instance();
		forever {
			if(mw.recv(datos)) {
				MensajeAplicacion msj;
				msj.loadFromArray(datos);
				if(msj.getEncabezado().getTipoMensaje()==INICIO_JUEGO) {
					logger << LogType::LOG_DEBUG_1 << "mensaje de inicio de juego recibido!!!" << logger.endl;
					return true;
				} else {
					logger << LogType::LOG_WARNING <<
							"se recibió un mensaje que no corresponde al "
							"tipo INICIO_JUEGO y por lo tanto será "
							"descartado" << logger.endl;
				}
			} else {
				logger << LogType::LOG_ERROR << "error recibiendo mensaje de middleware, "
						"cancelando jugador" << logger.endl;
				result = cancelar_jugador_1(
						jugador.idJugador.getId(),
						jugador.idJugador.getContainerId(),
						client);
				return false;
			}
		}
	} else {
		return false;
	}

	return false;
}

void DirectorioImpl::convertir(ArrayUniversoRPC* arr, t_listaUniversos& lista) {
	for(u_int i=0; i<arr->ArrayUniversoRPC_len; i++) {
		InformacionCreacionUniverso universo;
		universo._idUniverso = arr->ArrayUniversoRPC_val[i].id;
		universo._minPlanetas = arr->ArrayUniversoRPC_val[i].min;
		universo._maxPlanetas = arr->ArrayUniversoRPC_val[i].max;
		universo._timeout = arr->ArrayUniversoRPC_val[i].ttl;
		lista.push_back(universo);
	}
}

t_listaUniversos DirectorioImpl::listarUniversosAbiertos() {
	ArrayUniversoRPC *result;
	t_listaUniversos universos;

	result = listar_universos_abiertos_1(client);

	if(result == (ArrayUniversoRPC*)NULL) {
		return t_listaUniversos();
	} else {
		convertir(result, universos);
		free(result->ArrayUniversoRPC_val);
	}

	return universos;
}

t_listaUniversos DirectorioImpl::listarUniversosCerrados() {
	ArrayUniversoRPC *result;
	t_listaUniversos universos;

	result = listar_universos_cerrados_1(client);

	if(result == (ArrayUniversoRPC*)NULL) {
		return t_listaUniversos();
	} else {
		convertir(result, universos);
		free(result->ArrayUniversoRPC_val);
	}

	return universos;
}

t_listaJuegos DirectorioImpl::listarJuegosAbiertos() {
	ArrayJuegoRPC *result;
	t_listaJuegos juegos;

	result = listar_juegos_abiertos_1(client);

	if(result == (ArrayJuegoRPC*)NULL) {
		return t_listaJuegos();
	} else {
		for(u_int i=0; i<result->ArrayJuegoRPC_len; i++) {
			JuegoDTO juego;
			juego.idJuego = result->ArrayJuegoRPC_val[i].id;
			juego.idUniverso = result->ArrayJuegoRPC_val[i].universo.id;
			juego.min = result->ArrayJuegoRPC_val[i].min;
			juego.max = result->ArrayJuegoRPC_val[i].max;
			juegos.push_back(juego);
		}
		free(result->ArrayJuegoRPC_val);
	}

	return juegos;
}

bool DirectorioImpl::destruirJuego(const JuegoDTO& juego) {
	bool_t *result;

	result = destruir_juego_1(
			juego.idJuego.getId(),
			client);

	return (result!=(bool_t*)NULL && *result==TRUE);
}

InformacionPlaneta* DirectorioImpl::getInformacionPlaneta(
		InformacionPlaneta* infoPlaneta) {
	PlanetaRPC *result;

	result = get_informacion_planeta_1(
			infoPlaneta->_identificador.getIdUniverso(),
			infoPlaneta->_identificador.getId(),
			client);

	if(result!=NULL && (T_ID_PLANETA)result->id!=IdPlaneta::ID_UNDEFINED) {
		infoPlaneta->_tamanio = result->tamanio;
		infoPlaneta->_posicion = Posicion(result->posx, result->posy);
	}

	return infoPlaneta;
}

Direccion DirectorioImpl::getDireccionPlaneta(IdPlaneta idPlaneta) {
	u_int *result;

	result = resolver_planeta_1(
			idPlaneta.getIdUniverso(),
			idPlaneta.getId(),
			client);

	return result!=NULL ? *result : DIR_INVALIDA;
}

t_listaPlanetas DirectorioImpl::listarPlanetas(IdUniverso idUniverso) {
	ArrayPlanetaRPC *result;
	t_listaPlanetas planetas;

	result = listar_planetas_1(idUniverso.getId(), client);

	if(result == (ArrayPlanetaRPC*)NULL) {
		return t_listaPlanetas();
	} else {
		for(u_int i=0; i<result->ArrayPlanetaRPC_len; i++) {
			InformacionPlaneta planeta;
			planeta._identificador = IdPlaneta(
					result->ArrayPlanetaRPC_val[i].id,
					idUniverso.getId());
			planeta._posicion = Posicion(
					result->ArrayPlanetaRPC_val[i].posx,
					result->ArrayPlanetaRPC_val[i].posy);
			planeta._tamanio = result->ArrayPlanetaRPC_val[i].tamanio;
			planeta._duenio = IdJugador::ID_UNDEFINED;
			planeta._cantidadNaves = 0;
			planetas.push_back(planeta);
		}
		free(result->ArrayPlanetaRPC_val);
	}

	return planetas;
}

}
